using System;
using System.Linq;
using System.Linq.Expressions;
using NHibernate;
using NHibernate.Linq;
using NHibernate.Transform;

namespace NHibernateInsight.Tests.Insights.Linq.Extensions
{
    public static class LinqExtensionsForNHibernate
    {
        public static ICriteria AsCriteriaQuery<T>(this IQueryable<T> queryable)
        {
            return _NhQueryableFrom(queryable).AsCriteriaQuery();            
        }

        public static IQueryable<T> DistinctRoot<T>(this IQueryable<T> queryable)
        {
            return _NhQueryableFrom(queryable).DistinctRoot();
        }
        public static IQueryable<T> Cached<T>(this IQueryable<T> queryable)
        {
            return _NhQueryableFrom(queryable).Cached();
        }
        public static IQueryable<T> Cached<T>(this IQueryable<T> queryable, string cacheRegion)
        {
            return _NhQueryableFrom(queryable).Cached(cacheRegion);
        }
        public static IQueryable<T> CacheMode<T>(this IQueryable<T> queryable, CacheMode cacheMode)
        {
            return _NhQueryableFrom(queryable).CacheMode(cacheMode);
        }
        public static IQueryable<T> Flush<T>(this IQueryable<T> queryable, FlushMode flushMode)
        {
            return _NhQueryableFrom(queryable).Flush(flushMode);
        }
        public static IQueryable<T> Lock<T>(this IQueryable<T> queryable, LockMode lockMode)
        {
            return _NhQueryableFrom(queryable).Lock(lockMode);
        }
        public static IQueryable<T> FetchSize<T>(this IQueryable<T> queryable, int fetchSize)
        {
            return _NhQueryableFrom(queryable).FetchSize(fetchSize);
        }
        public static IQueryable<T> Timeout<T>(this IQueryable<T> queryable, int timeout)
        {
            return _NhQueryableFrom(queryable).Timeout(timeout);
        }
        public static IQueryable<T> Expand<T, K, L>(this IQueryable<T> queryable, Expression<Func<T, K>> selector, SubpathBuilder<L> subpaths)
        {
            return _NhQueryableFrom(queryable).Expand(selector, subpaths);
        }
        public static IQueryable<T> Expand<T, K>(this IQueryable<T> queryable, Expression<Func<T, K>> selector)
        {
            return _NhQueryableFrom(queryable).Expand(selector);
        }
        public static IQueryable<T> Expand<T, K, L>(this IQueryable<T> queryable, Expression<Func<T, K>> selector, SubpathBuilder<L> subpaths, FetchMode fetchMode)
        {
            return _NhQueryableFrom(queryable).Expand(selector, subpaths, fetchMode);
        }
        public static IQueryable<T> Expand<T, K>(this IQueryable<T> queryable, Expression<Func<T, K>> selector, FetchMode fetchMode)
        {
            return _NhQueryableFrom(queryable).Expand(selector, fetchMode);
        }

        private static INHibernateQueryable<T> _NhQueryableFrom<T>(IQueryable<T> queryable)
        {
            var nhQueryable = queryable as INHibernateQueryable<T>;
            if (nhQueryable == null)
                throw new InvalidOperationException("Underlying type behind this IQueryable is not INHibernateQueryable!");

            return nhQueryable;
        }
    }
}